#pragma once

// 抽象落地模块:写入到哪里
// 根据落地方向派生子类
// 使用工厂模式进行创建与表示的分离
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
#include <cassert>
#include <sstream>
#include "util.hpp"

namespace nhylog
{

    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;

        LogSink() {}
        virtual ~LogSink() {} // 便于以后扩展
    public:
        virtual void log(const char *data, size_t len) = 0;
    };

    // 标准输出
    class StdoutSink : public LogSink
    {
    public:
        void log(const char *data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };
    // 文件输出
    class FileSink : public LogSink
    {
    public:
        // 记下文件路径，保存操作句柄，防止每次都打开文件。
        FileSink(const std::string &pathname) : pathname_(pathname)
        {
            // 1 创建文件所在目录
            util::File::createDirectory(util::File::path(pathname));
            // 2 创建打开日志文件
            ofs_.open(pathname, std::ios::binary | std::ios::app);
        }
        void log(const char *data, size_t len) override
        {
            ofs_.write(data, len);
            assert(ofs_.good());
        }

    private:
        std::string pathname_;
        std::ofstream ofs_;
    };
    // 滚动文件(按照大小滚动)
    class RollBySizeSink : public LogSink
    {
    public:
        RollBySizeSink(const std::string &basename, size_t max_fsize)
            : basename_(basename), max_fsize_(max_fsize), cur_fisze_(0),
              file_count_(0)
        {
            std::string pathname = basename + createNewFile();

            // 1 创建文件所在目录
            util::File::createDirectory(util::File::path(pathname));
            // 2 创建打开日志文件
            ofs_.open(pathname, std::ios::binary | std::ios::app);
        }
        // 写入前判断大小，超过要切换文件
        void log(const char *data, size_t len) override
        {
            if (cur_fisze_ >= max_fsize_)
            {
                ofs_.close();
                std::string pathname = basename_ + createNewFile();

                // 2 创建打开日志文件
                ofs_.open(pathname, std::ios::binary | std::ios::app);

                // 3 大小清零
                cur_fisze_ = 0;
            }

            ofs_.write(data, len);
            assert(ofs_.good());
            cur_fisze_ += len;
        }

    private:
        std::string createNewFile()
        {
            time_t t = util::Date::now();
            struct tm lt;
            localtime_r(&t, &lt);
            std::stringstream pathname;
            pathname << lt.tm_year + 1900;
            pathname << lt.tm_mon + 1;
            pathname << lt.tm_mday;
            pathname << lt.tm_hour;
            pathname << lt.tm_min;
            pathname << lt.tm_sec;
            pathname << "-" << file_count_++;
            pathname << ".log";

            return pathname.str();
        }

    private:
        // 同一时间区别文件
        int file_count_ = 0;
        // 基准名字 + 扩展名
        std::string basename_;
        std::ofstream ofs_;
        size_t max_fsize_;
        size_t cur_fisze_;
    };

    class SinkFactory
    {
    public:
        // 便于用户自己扩展，简单工厂要修改源代码。
        template <typename SinkType, typename... Args>
        static LogSink::ptr create(Args &&...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };

}
